home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 283_01 / fafnir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-12-16  |  47.6 KB  |  1,898 lines

  1. /* fafnir.c -- general purpose forms dragon, with Elysian Fields
  2.                5/10/88, 8/20/88, 9/10/88, 9/24/88, by d.c.oshel
  3.                */
  4.  
  5.  
  6. #include "ciao.h"
  7. #include "keys.h"
  8. #include "fafnir.h"
  9. #include <stdarg.h>
  10.  
  11. #define FVERSION "Fafnir v. 3.0"
  12.  
  13. #define VERSION "╡^2 Elysian Fields v. 3.0 ^0╞══════════" 
  14. /*              "▒▒▒▒▒▒▒▒░░░░░▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒▌",*/
  15.  
  16.  
  17. /* value returned from user dialogue boxes only */
  18. #define EDIT_FORM 0
  19.  
  20.  
  21. /* =====  Common Default Field Masks  ===== */
  22.  
  23. char zssn[]   = "000-00-0000";
  24. char zblank[] = "";
  25. char zdate[]  = "00/00/00";
  26. char zfone[]  = "(   )    -    ";  /* 14 chars */
  27. char zeroes[] = "0000000000";  /* ten zeroes */
  28. char zdol[]   = "0.00";
  29.  
  30.  
  31.  
  32.  
  33. /*================  Global Character Validaters  ==================*/
  34.  
  35.  
  36. /* allow any printable char */
  37. unsigned cgood( unsigned c )
  38. {
  39.     return ( (isprint(c))? c: 0 );
  40. }
  41.  
  42.  
  43. /* allow only uppercase alpha, else any printable */
  44. unsigned c2upr( unsigned c )
  45. {
  46.     return ( (isprint(c))? toupper(c): 0 );
  47. }
  48.  
  49.  
  50. /* allow only digits 0..9 */
  51. unsigned cnmrc( unsigned c )
  52. {
  53.     return ( (isdigit(c))? c: 0 );
  54. }
  55.  
  56.  
  57. /* allow only 0..9, blanks and hyphen */
  58. unsigned cnzip( unsigned c )
  59. {
  60.     return ( (isdigit(c) || c == ' ' || c == '-') ? c : 0 );
  61. }
  62.  
  63.  
  64. /* allow only chars appropriate in dollar amounts */
  65. unsigned cdoll( unsigned c )
  66. {
  67.     if ( isdigit(c) || c == '.' || c == ',' || 
  68.            c == '$' || c == ' ' || c == '-' )
  69.         return (c);
  70.     else
  71.         return (0);
  72. }
  73.  
  74.  
  75. /* return Y on T,Y,1, return N on F,N,0 */
  76. unsigned ctfyn( unsigned c )
  77. {
  78.     c = toupper(c);
  79.     if ( c == 'T' || c == 'Y' || c == '1' )
  80.         return ('Y');
  81.     else if ( c == 'F' || c == 'N' || c == '0' )
  82.         return ('N');
  83.     else
  84.         return (0);
  85. }
  86.  
  87.  
  88. /* fills "(   )    -    " zfone mask with "(123) 678-ABCD" */
  89. unsigned cfone( unsigned c, int pos )  
  90. {
  91.     if ( pos == 0 || pos == 4 || pos == 5 || pos == 9 )
  92.         return (0);
  93.     else if (pos < 10)
  94.         return ( cnzip( c ) );
  95.     else if ( isalnum( c ) || c == ' ' )
  96.         return ( toupper( c ) );
  97.     else
  98.         return (0);  /* does not allow SPC in some positions */
  99. }
  100.  
  101. /*======================  Global Field Validaters  ==================*/
  102.  
  103.  
  104. /* === vdate(): Date Validater === */
  105.  
  106. static int goodday( int m, int d, int y )
  107. {
  108.     static int days[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
  109.     static char *mo_names[] = {
  110.         NULL, 
  111.         "January", "February", "March", "April", "May", "June",
  112.         "July", "August", "September", "October", "November", "December"
  113.     };
  114.  
  115.     int leapyear;
  116.  
  117.     if ( m < 1 || m > 12 )
  118.     {
  119.         boxmsg("MM/dd/yy range error: MM = %d?", m );
  120.         return (0);
  121.     }
  122.  
  123.     leapyear = y % 4;  /* fails in 2000 A.D. */
  124.     days[2] = leapyear? 28 : 29;
  125.  
  126.     if ( d < 1 || d > days[ m ] )
  127.     {
  128.         boxmsg("%s %d?  %d days hath %s...", mo_names[m], d, days[m], mo_names[m] );
  129.         return (0);
  130.     }
  131.     return (1);
  132. }
  133.  
  134. /* is the date valid? 
  135.    */
  136. int vdate( char *p, int x, int y, int len )
  137. {
  138.     int n,m,d,yr;
  139.     char date[9];
  140.  
  141.     if (*p == ' ')
  142.     {
  143.         cf_date( date, 2 );
  144.         boxmsg( "MM/DD/YY  E.g., today is %s", date );
  145.         return (0);
  146.     }
  147.  
  148.     m  = atoin( p + 0, 2 );            /* "00/00/00" */
  149.     d  = atoin( p + 3, 2 );
  150.     yr = atoin( p + 6, 2 );
  151.  
  152.     n = goodday(m,d,yr);            /* prints range error messages */
  153.     return (n);
  154. }
  155.  
  156.  
  157. /* "validate" date field by setting it to today's date
  158. */
  159. int vtoday( char *p, int x, int y, int len )
  160. {
  161.     char d[9];
  162.     cf_date( d, 2 );
  163.     memcpy( p,d,8 );
  164.     return (1);
  165. }
  166.  
  167.  
  168. static char doll[] = "Enter $ amount";
  169.  
  170. /* field init, left justify $0.00 in field */
  171. int lj_dol ( char *p, int x, int y, int len )
  172. {
  173.     long n;
  174.     char *q,*r;
  175.     char temp[48];
  176.  
  177.     if (*p == ' ')
  178.     {
  179.         boxmsg( doll );
  180.         return (0);
  181.     }
  182.  
  183.     n = atodoln( p, len );  /* interpret field contents */
  184.     q = dlrcvt( n );        /* make it canonical 0.00 format */ 
  185.     r = strchr( q, '\0' );  /* remove trailing minus or blank */
  186.     r--;
  187.     *r = '\0';
  188.  
  189.     if ( n < 0L )
  190.     {
  191.         r = malloc(len);
  192.         strcpy( r, "-" );
  193.         strcat( r,q );
  194.         sprintf( temp, "%-*s", len, r );  /* left justify, <--- */
  195.         free(r);
  196.     }
  197.     else
  198.         sprintf( temp, "%-*s", len, q );  /* left justify, <--- */
  199.  
  200.     if ( strlen(temp) != len )  /* do not alter field */
  201.         return (0);
  202.     else
  203.         strcpy( p,temp );
  204.  
  205.     return (1);
  206. }
  207.  
  208.  
  209.  
  210. /* field validate, right justify user input as a canonical $0.00 dollar 
  211.    amount (adds missing cents digits!) 
  212.    */
  213.  
  214. int rj_dol ( char *p, int x, int y, int len )
  215. {
  216.     long n;
  217.     char *q,*r;
  218.     char temp[48];
  219.  
  220.     if (*p == ' ')
  221.     {
  222.         boxmsg( doll );
  223.         return (0);
  224.     }
  225.  
  226.     n = atodoln( p, len );  /* interpret field contents */
  227.     q = dlrcvt( n );        /* make it canonical 0.00 format */ 
  228.     r = strchr( q, '\0' );  /* remove trailing minus or blank */
  229.     r--;
  230.     *r = '\0';
  231.  
  232.     if ( n < 0L )
  233.     {
  234.         r = malloc(len);
  235.         strcpy( r, "-" );
  236.         strcat( r,q );
  237.         sprintf( temp, "%*s", len, r );  /* right justify, ---> */
  238.         free(r);
  239.     }
  240.     else
  241.         sprintf( temp, "%*s", len, q );   /* right justify, ---> */
  242.  
  243.     if ( strlen(temp) != len )  /* do not alter field */
  244.         return (0);
  245.     else
  246.         strcpy( p,temp );
  247.  
  248.     return (1);
  249. }
  250.  
  251.  
  252. static char *tf[] = {
  253.     "Yes",
  254.     "No"
  255. };
  256. int vtrufal( char *p, int x, int y, int len )
  257. {
  258.     if (*p == ' ')
  259.     {
  260.         memcpy( p, tf[ select(x,y,tf,2) ], 1 );
  261.     }
  262.     return (1);
  263. }
  264.  
  265. /* ================  Other Global Utilities ================= */
  266.  
  267. static PTR_TO_CRASH_FN cleanup = NO_CRASH_FN;
  268.  
  269.                                    /*....v....1....v....2....v....3.*/
  270. static char DamageControlBuffer[] = "<<<< Fafnir Damage Control >>>>";
  271. static char DamageControlMsg[]    = "<<<< Fafnir Damage Control >>>>";
  272. static char Damage80Slop[]        = "Fafnir by Oshel"; /* 32+32+16 makes 80 for slop */
  273.  
  274. /* a recommended bomb function, user may #define bomb(X) bomb0(X)
  275.    */
  276.  
  277.  
  278. void bomb0( char *msg, ... )
  279. {
  280.     va_list arg_ptr;
  281.  
  282.     /* call user's fatal error shutdown routine, if set */
  283.  
  284.     if ( cleanup != NO_CRASH_FN )
  285.         (*cleanup)();
  286.  
  287.     /* shut ourselves down */
  288.  
  289.     setsynch(1);
  290.     gotoxy(0,24);
  291.     vid_exit();
  292.  
  293.     /* print the terminating diagnostic message */
  294.  
  295.     printf("\a\n*** error: ");
  296.     va_start( arg_ptr, msg );
  297.     vprintf( msg, arg_ptr );
  298.     va_end( arg_ptr );
  299.     printf("\n");
  300.  
  301.     if (strcmp(DamageControlBuffer,DamageControlMsg) != 0)
  302.         printf( "\n*** assignment to unallocated field\n");
  303.  
  304.     /* don't allow batch to take over before user has had a chance to
  305.        see and record the message
  306.        */
  307.     while ( kbhit() )
  308.         getch();
  309.     printf( "\n*** press any key " );
  310.     getch();
  311.  
  312.     /* and die */
  313.  
  314.     exit (255);
  315. }
  316.  
  317.  
  318. void set_crash_function( PTR_TO_CRASH_FN x )
  319. {
  320.     *cleanup = x;
  321. }
  322.  
  323.  
  324. void exit_fafnir( void )
  325. {
  326.     if ( strcmp(DamageControlBuffer,DamageControlMsg) != 0 )
  327.         bomb0( FVERSION );
  328. }
  329.  
  330. /* end of error handlers */
  331.  
  332.  
  333.  
  334.  
  335. /* returns the value of len (or fewer) digits in p as a signed int 
  336.    */
  337. int atoin( char *p, int len )
  338. {
  339.     int i;
  340.     int n, sign;
  341.  
  342.     n = 0;
  343.     sign = 1;
  344.     for ( i = 0; i < len && *p; i++, p++ )
  345.     {
  346.         if ( *p == '-' )
  347.             sign = -1;
  348.         else if ( isdigit( *p ) )
  349.         {
  350.             n *= 10;
  351.             n += (*p - '0');
  352.         }
  353.     }
  354.     return ( n * sign );
  355. }
  356.  
  357.  
  358.  
  359. /* returns the value of len (or fewer) digits in p as a signed long int 
  360.    */
  361. long atoln( char *p, int len )
  362. {
  363.     int i;
  364.     long n, sign;
  365.  
  366.     n = 0L;
  367.     sign = 1L;
  368.     for ( i = 0; i < len && *p; i++, p++ )
  369.     {
  370.         if ( *p == '-' )
  371.             sign = -1L;
  372.         else if ( isdigit( *p ) )
  373.         {
  374.             n *= 10L;
  375.             n += (long)(*p - '0');
  376.         }
  377.     }
  378.     return ( n * sign );
  379. }
  380.  
  381.  
  382. /* atodoln() returns the value of len (or fewer) digits in p as a signed 
  383.    "dollar long" int, i.e., the string "$1.00" returns 100L, normalized
  384.    to n * 100 cents
  385.  
  386.    allows free-form user cents fields, i.e., 
  387.       $100 is $100. is $100.0 is $100.00 is $100.000 (mills digit ignored!)
  388.       all these cases return 10000L, equivalent to $100.00
  389.    */
  390. long atodoln( char *p, int len )
  391. {
  392.     int i, dotflag, count;
  393.     long n, sign;
  394.  
  395.     for ( count = dotflag = i = 0, n = 0L, sign = 1L; 
  396.           *p && (i < len) && (count < 2); 
  397.           i++, p++ )
  398.     {
  399.         if ( isdigit(*p) )
  400.         {
  401.             n *= 10L;
  402.             n += (long) (*p - '0');
  403.             if ( dotflag )
  404.             {
  405.                 ++count;
  406.             }
  407.         }
  408.         else if ( *p == '-' )
  409.         {
  410.             sign = -1L;
  411.         }
  412.         else if ( *p == '.' )
  413.         {
  414.             dotflag = 1;
  415.         }
  416.     }
  417.  
  418.     /* adjust 1 dollar to 100 cents precision */
  419.  
  420.     for ( ; count < 2; count++ )
  421.     {
  422.         n *= 10L;
  423.     }
  424.     return ( n * sign );
  425. }
  426.  
  427. /* put a transient error message in the middle of the screen; vanishes in
  428.    about 8 seconds, or if user presses a key
  429.    */
  430. void boxmsg( char *msg, ... )
  431. {
  432.     char far *p;
  433.     union REGS x;
  434.     int n;
  435.     va_list arg_ptr;
  436.     char buf[128];
  437.  
  438.     va_start( arg_ptr, msg );
  439.     vsprintf( buf, msg, arg_ptr );
  440.     va_end(arg_ptr);
  441.  
  442.     p = savescreen( &x );
  443.  
  444.     hidecursor();
  445.     reverse_video();
  446.     n = ((80 - strlen(buf)) / 2) - 4;
  447.     windowbox( n, 9, n + strlen(buf) + 3, 11 );
  448.     gotoxy( 2,1 );
  449.     wputs( buf );
  450.     sleep( 40 );
  451.  
  452.     restorescreen( p,&x );
  453.  
  454. }
  455.  
  456. /* same, but overwrites current screen, not timed */
  457. void nboxmsg( char *msg, ... )
  458. {
  459.     union REGS x;
  460.     int n,a,b,c,d;
  461.     va_list arg_ptr;
  462.     char buf[128];
  463.  
  464.     va_start( arg_ptr, msg );
  465.     vsprintf( buf, msg, arg_ptr );
  466.     va_end(arg_ptr);
  467.  
  468.     savecursor( &x );
  469.  
  470.     hidecursor();
  471.     reverse_video();
  472.     n = ((80 - strlen(buf)) / 2) - 4;
  473.     windowbox( n, 9, n + strlen(buf) + 3, 11 );
  474.     gotoxy( 2,1 );
  475.     wputs( buf );
  476.  
  477.     restcursor( &x );
  478. }
  479.  
  480.  
  481.  
  482. static void ScrnMsg( char *q, int x, int y, int len )
  483. {
  484.     /* this is a local function because the next assumes both len's 
  485.        equal and non-printing portions of the string blank-filled,
  486.        true for sure only in Fafnir */
  487.  
  488.     MSJ_SetFldAttr( SPC, tm+y, lm+x, vid_attr, len, &video );
  489.     MSJ_DispMsgLen( q, tm+y, lm+x, len, &video );
  490. }
  491.  
  492.  
  493. static void DispIntense( char *p, int x, int y, int len )
  494. {
  495.     /* this is a local function because the next assumes both len's 
  496.        equal and non-printing portions of the string blank-filled,
  497.        true for sure only in Fafnir */
  498.  
  499.     /* set intense video attr */
  500.     MSJ_SetFldAttr( SPC, tm+y, lm+x, vid[1], len, &video );
  501.     MSJ_DispMsgLen( p, tm+y, lm+x, len, &video );
  502. }
  503.  
  504.  
  505.  
  506. /* select0() does NOT adjust coordinates, or save the screen 
  507.    */
  508.  
  509. int select0( int topx, int topy, char *menu[], int menu_size )
  510. {
  511.     int line, width, limit, frame;
  512.     char *q;
  513.     unsigned c;
  514.  
  515.     setsynch(0);
  516.     hidecursor();
  517.     intense_video();
  518.  
  519.     limit = min( 16, menu_size );
  520.  
  521.     for ( width = line = 0; line < menu_size; line++ )
  522.     {
  523.         q = menu[ line ];
  524.         width = max( width, strlen(q) );
  525.     }
  526.  
  527.     windowbox( topx, topy, (topx + width + 1), (topy + limit - 1) );
  528.  
  529.  
  530. zoo:
  531.     for ( line = 0; line < limit; line++ )
  532.     {
  533.         q = menu[ line ];
  534.         MSJ_SetFldAttr( SPC, tm+line, lm+0, vid[1],    width + 2, &video );
  535.         MSJ_DispMsgLen(   q, tm+line, lm+1, strlen(q), &video );
  536.     }
  537.  
  538.     frame = line = 0;
  539.     while (1)
  540.     {
  541.         q = menu[ (frame + line) % menu_size ];
  542.  
  543.         MSJ_SetFldAttr( SPC, tm+line, lm+0, vid[2],    width + 2, &video );
  544.         MSJ_DispMsgLen(   q, tm+line, lm+1, strlen(q), &video );
  545.  
  546.         c = keyin( screenwait );
  547.  
  548.         MSJ_SetFldAttr( SPC, tm+line, lm+0, vid[1],    width + 2, &video );
  549.         MSJ_DispMsgLen(   q, tm+line, lm+1, strlen(q), &video );
  550.  
  551.         switch ( c )
  552.         {
  553.         default:
  554.                 boxmsg( "Arrow keys move bar to your choice, Enter selects" );
  555.                 break;
  556.  
  557.         case CR:
  558.                 goto exit;
  559.                 break;
  560.  
  561.         case UP:
  562.         case LF:
  563.         case PGUP:
  564.         case '4':
  565.         case '8':
  566.                 --line;
  567.                 if ( line < 0 )
  568.                 {
  569.                     scrolldn();
  570.                     line = 0;
  571.                     --frame;
  572.                     if ( frame < 0 )
  573.                     {
  574.                         frame = menu_size - 1;
  575.                     }
  576.                 }
  577.                 break;
  578.  
  579.         case HOME:
  580.         case '7':
  581.                 goto zoo;
  582.                 break;
  583.  
  584.         case DN:
  585.         case RT:
  586.         case PGDN:
  587.         case '2':
  588.         case '6':
  589.                 if ( line == limit - 1)
  590.                 {
  591.                     scrollup();
  592.                     ++frame;
  593.                     if ( frame > menu_size - 1 )
  594.                     {
  595.                         frame = 0;
  596.                     }
  597.                 }
  598.                 else 
  599.                     ++line;
  600.                 break;
  601.         }
  602.     }
  603.  
  604.     exit:
  605.     return ( ((frame + line) % menu_size) );
  606. }
  607.  
  608.  
  609. /* select() saves the screen, restores it on exit, adjusts coordinates
  610.    if necessary to fit on screen
  611.    */
  612.  
  613. int select( int topx, int topy, char *menu[], int menu_size )
  614. {
  615.     char far *p;
  616.     union REGS x;
  617.     char *q;
  618.     int result, limit, width, line;
  619.  
  620.     p = savescreen(&x);
  621.  
  622.     limit = min( 16, menu_size );
  623.  
  624.     for ( width = line = 0; line < menu_size; line++ )
  625.     {
  626.         q = menu[ line ];
  627.         width = max( width, strlen(q) );
  628.     }
  629.  
  630.     while (topy && (( topy + limit + 2 ) >= 23))
  631.         topy -= 1;
  632.  
  633.     if ( topx > 39 )
  634.         topx = 39;
  635.     while (topx && (( topx + width + 4 ) >= 79))
  636.         topx -= 1;
  637.  
  638.     result = select0( topx, topy, menu, menu_size );
  639.     restorescreen(p,&x);
  640.     return (result);
  641. }
  642.  
  643.  
  644.  
  645. unsigned screenroutine( unsigned (*fn)(char *), char *msg, ... )
  646. {
  647.     char far *p;
  648.     union REGS x;
  649.     unsigned result;
  650.     va_list arg_ptr;
  651.     char buf[128];
  652.  
  653.     va_start( arg_ptr, msg );
  654.     vsprintf( buf, msg, arg_ptr );
  655.     va_end(arg_ptr);
  656.  
  657.     p = savescreen(&x);
  658.     result = (*fn)( buf );
  659.     restorescreen( p,&x );
  660.     return (result);
  661. }
  662.  
  663.  
  664.  
  665. /* ================  Local Utilities ================== */
  666.  
  667. static char *startup[] = {
  668.     "Edit this record",
  669.     "Skip this record",
  670.     "Delete this record",
  671.     "Stop searching"
  672. };
  673.  
  674. static int opening_dialogue( void )
  675. {
  676.     bopbleet();
  677.     switch ( select( (80-strlen(startup[2]))/2, 1, startup, 4 ) )
  678.     {
  679.     default:
  680.     case 0:  return ( EDIT_FORM );
  681.     case 1:  return ( SKIP_FORM );
  682.     case 2:  return ( DELETE_FORM );  /* will ask about this! */
  683.     case 3:  return ( STOP_SEARCH );
  684.     }
  685. }
  686.  
  687.  
  688. char *Fafnir_form_exit_actions[] = {  /* GLOBAL, 12/4/88, dco */
  689.     "Continue editing",
  690.     "Save this record",
  691.     "Quit"
  692. };
  693.  
  694. static int decide_form_exit( void )
  695. {
  696.     bopbleet();
  697.     switch ( select( (80-strlen(Fafnir_form_exit_actions[0]))/2, 1, Fafnir_form_exit_actions, 3 ) )
  698.     {
  699.     default:
  700.     case 0:  return ( EDIT_FORM );
  701.     case 1:  return ( SAVE_FORM );
  702.     case 2:  return ( SKIP_FORM );
  703.     }
  704. }
  705.  
  706. static char *help[] = {             
  707. /*   ....v....1....v....2....v....3... */  /* 177, 176, 182, 198, 205, 221 */
  708.     VERSION,
  709.     "▒▒▒▒▒▒▒▒░░░░░▒▒▒▒░░░▒▒▒▒▒▒▒▒▒▒▒▒▌",
  710.     "░▌Press ^1Enter^0 or ^1Esc^0 when done ░▌",    
  711.     "▒▌·········Other Keys··········▒▌",
  712.     "░▌    ^1F1^0 input assistance      ░▌",
  713.     "░▌    ^1F2^0 reset field & exit    ░▌",
  714.     "░▌    ^1F3^0 reset current field   ░▌",
  715.     "░▌    ^1F4^0 musical gadgets +/-   ░▌",
  716.     "░▌    ^1F10^0 displays this box    ░▌",
  717.     "▒▌        Home ^1^0 PgUp          ▒▌",
  718.     "▒▌           ^1^0 ^1·^0 ^1^0             ▒▌",      
  719.     "▒▌        End  ^1^0 PgDn          ▒▌",
  720.     "▒▌  In text fields: ^1Ins^0, ^1Del^0,  ░▌",
  721.     "░▌  ^1F9^0 erases to end of field  ▒▌",
  722.     "▒▒▒▒░░▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░▒▒░░░▒▒▒▒▌",
  723.     NULL
  724. };
  725.  
  726. static void version_info( void )
  727. {
  728.     char **p;
  729.     int y,t;
  730.     char far *fp;
  731.     union REGS x;
  732.  
  733.     fp = savescreen(&x);
  734.     hidecursor();
  735.     normal_video();
  736.     t = getsynch();
  737.     setsynch(0);
  738.     for ( y = 0, p = help; *p != NULL; p++,y++ )
  739.     {
  740.         gotoxy(1,y);
  741.         wputs(*p);
  742.     }
  743.     setsynch(t);
  744.     sleep( 200 );
  745.     restorescreen(fp,&x);
  746. }
  747.  
  748. static char dynmemerr[] = "out of memory";
  749.  
  750. /* PrepScreen() reads a 4000-byte screen file from disk to a buffer, and 
  751.    returns a char far * to the buffer which contains the screen.
  752.  
  753.    Screen files must be created using ES.EXE, or any similar program which
  754.    writes video text page 0 to a disk file.
  755.    */
  756.  
  757. char far * PrepScreen( char *screen )
  758. {
  759.      char far *p;
  760.      FILE *fptr;
  761.      int i, a;
  762.  
  763.      if ( screen == (char *) NULL )
  764.         return ( (char far *) NULL );
  765.  
  766.      p = _fmalloc( 4000 );
  767.      if ( p == (char far *) NULL )
  768.      {
  769.         bomb0("prep screen: %s", dynmemerr);
  770.      }
  771.  
  772.      if ((fptr = fopen(screen,"rb")) == NULL)
  773.      {
  774.         _ffree(p);
  775.          bomb0("can't find screen file: %s\n",screen);
  776.      }
  777.      else
  778.      {
  779.         for (i = 0; i < 4000; i++)
  780.         {
  781.             p[i] = fgetc( fptr );    /* character */
  782.             a    = fgetc( fptr );    /* attribute */
  783.  
  784.             if (ferror(fptr))
  785.             {
  786.                 _ffree(p);
  787.                 bomb0("can't read screen file: %s\n",screen);
  788.             }
  789.  
  790.             /* translate monochrome screen attributes to color */
  791.  
  792.             if ( video.mode == 3 )  /* running color on CGA? */
  793.             {
  794.                 switch ( a )
  795.                 {
  796.                 default:
  797.                 case '\007':            /* NRM */
  798.                         a = vid[0];
  799.                         break;
  800.                 case '\017':            /* BRN */
  801.                         a = vid[1];
  802.                         break;
  803.                 case '\160':            /* RVR */
  804.                         a = vid[2];
  805.                         break;
  806.                 case '\360':            /* BLR */
  807.                         a = vid[3];
  808.                         break;
  809.                 case '\001':            /* UNL */
  810.                         a = vid[4];
  811.                         break;
  812.                 case '\011':            /* BRU */
  813.                         a = vid[5];
  814.                         break;
  815.                 case '\207':            /* BLN */
  816.                         a = vid[6];
  817.                         break;
  818.                 case '\201':            /* BLU */
  819.                         a = vid[7];
  820.                         break;
  821.                 case '\217':            /* BBN */
  822.                         a = vid[8];
  823.                         break;
  824.                 case '\211':            /* BBU */
  825.                         a = vid[9];
  826.                         break;
  827.                 }
  828.             }
  829.             ++i;
  830.             p[i] = a;
  831.         }
  832.      }
  833.      fclose(fptr);
  834.      return (p);
  835. }
  836.  
  837.  
  838.  
  839. void PopScreen( char far *p )
  840. {
  841.     MSJ_MovBufScr( p, 0, 0, 2000, &video );
  842. }
  843.  
  844. /* ========================= Display Form Fields ======================= */
  845.  
  846.  
  847. /* DisplayForm returns nothing, assumes PrepScreen has been called to
  848. ** load a screen file, and that screen points to a buffer with screen image
  849. */
  850.  
  851. void DisplayForm( char far *screen, FORM_RULES form[], int NumFields )
  852. {
  853.     char **pp, *p;
  854.     int i, x, y, len, winlen, temp;
  855.     VALIDATER fi;
  856.  
  857.     if ( NumFields < 1 )
  858.         return; /* bad domain, assume caller will abort */
  859.     
  860.     if ( screen != (char far *) NULL )
  861.     {
  862.         PopScreen( screen );
  863.     }
  864.  
  865.     /* show all fields */
  866.  
  867.     temp = getsynch();
  868.     setsynch(0);
  869.  
  870.     for ( i = 0; i < NumFields; i++ )
  871.     {
  872.         pp  = form[i].fptr;
  873.         p   = *pp;
  874.         x   = form[i].x;
  875.         y   = form[i].y;
  876.         len = form[i].len;
  877.  
  878.         winlen = abs(len);
  879.         if ( len < 0 )
  880.             len = MAXVFLDLEN;
  881.  
  882.         if ( (fi = form[i].fi) != VNOP )
  883.             (*fi)( p,x,y,len );  /* set up virtual fields, etc. */
  884.  
  885.         DispIntense( p,x,y,winlen );
  886.     }
  887.  
  888.     setsynch(temp);
  889. }
  890.  
  891.  
  892. /* ========================= Edit Field and Form ======================= */
  893.  
  894. /* EditField can return ESC or CR, or F2, END, PGDN, PGUP, UP, DN
  895. ** exits via F2..DN do not validate the field, and do not change its contents
  896. ** exits via Esc or Enter, however, DO validate, and update if valid!
  897. ** Note: if ((*cvalid)(SPC,i)) in all positions, INS and DEL keys are enabled!
  898. ** F1 now requests assistance from (*fvalid)(), but does NOT exit EditField!
  899. ** F9 zaps to end-of-field, if INS and DEL keys are enabled.
  900. ** F3 is "undo", restores default field contents, does NOT exit.
  901. ** see also version_info(), above
  902. **
  903. ** New, 9/24/88, d.c.oshel -- if (len < 0), the field is treated like a
  904. **               variable length field padded right with blanks in a
  905. **               buffer MAXVFLDLEN chars long.  The edit field becomes a
  906. **               window len wide which "slides" over the longer edit buffer.
  907. **
  908. **               CAUTION:  fldptr  * M U S T *  point to a string which
  909. **               contains MAXVFLDLEN characters or blanks!  If INS and DEL
  910. **               keys can't be enabled (space not allowed in all positions),
  911. **               the field is treated like an ordinary field len chars wide.
  912. **
  913. **               AllocateFields() manages this detail, for forms only.
  914. */
  915.  
  916. unsigned EditField (char *fldptr,        /* data address */
  917.                     int x, int y,         /* screen coordinates, relative 0,0 */
  918.                     int len,            /* field length */ 
  919.                     int skip_to_next,     /* end-of-field handling flag */
  920.                     VALIDATER cvalid,    /* individual char validation */
  921.                     VALIDATER fvalid)    /* field validation */
  922. {
  923.      char *p, *q;
  924.      int i, field_cursor, temp;
  925.      int j, k, insdel, winlen, maxlen, echoline;
  926.      unsigned ch;
  927.  
  928.      /* bottom line save buffer if variable len edit */
  929.      char far *sylvia;
  930.  
  931.  
  932.      /* programmer stupidity check #1; is the field allocated? */
  933.  
  934.      if ( (fldptr == DamageControlBuffer) || (fldptr == (char *) NULL) )
  935.         bomb0("field not allocated" );
  936.  
  937.      temp = getsynch();
  938.      setsynch(0);
  939.  
  940.      echoline = 0;
  941.  
  942.      window_error:
  943.  
  944.      if ( len == 0 )
  945.      {
  946.         return ( END );  /* invalid length, same as slipped through field */
  947.      }
  948.      else if ( len < 0 ) /* request for sliding field input */
  949.      {
  950.         maxlen = MAXVFLDLEN;
  951.         winlen = abs(len);
  952.      }
  953.      else
  954.      {
  955.          winlen = maxlen = len;
  956.      }
  957.  
  958.      /* allocate space for copy of field string */
  959.  
  960.  
  961.      p = malloc( maxlen + 4 );        /* extra bytes are for luck */
  962.      if ( p == (char *) NULL )
  963.      {
  964.         /* unconditional program exit */
  965.         grunge:
  966.         bomb0("edit field: %s", dynmemerr);
  967.      }
  968.  
  969.      /*------------------------------------------------------------------*/
  970.      /*            DANGER!  Must null-terminate this buffer!             */
  971.      /*                                                                  */
  972.      /* Even though the actual field is not necessarily null-terminated, */
  973.      /* the working edit buffer * MUST * be a null-terminated string, to */
  974.      /* prevent accidental errors by sloppily-written field validaters!  */
  975.      /*------------------------------------------------------------------*/
  976.  
  977.      memset( p, 0, maxlen+4 ); 
  978.  
  979.      /* align window with edit buffer */
  980.  
  981.      q = p;
  982.  
  983.  
  984.  
  985.      do {  /* F1 assistance loop restarts here, if necessary */
  986.  
  987.      /* get copy of field string into work buffer */
  988.  
  989.      for ( insdel = 1, i = 0; i < maxlen; i++ )
  990.      {
  991.             /* do any edit character translations in mask up front;
  992.                for example, if user's keystrokes are translated to uppercase, 
  993.                the default field contents are presented in uppercase!
  994.                */
  995.             if ( cvalid == VNOP )
  996.             {
  997.                 ch = fldptr[i];
  998.             }
  999.             else
  1000.             {
  1001.                 ch = (*cvalid)( fldptr[i], i );  /* ch might be set to 0 */
  1002.  
  1003.                 /* i.f.f. SPC is ok for all i, enable INS and DEL keys
  1004.                    */
  1005.                 insdel &= ( (*cvalid)( SPC, i ) == SPC );
  1006.             }
  1007.              p[i] = ch? ch: fldptr[i];
  1008.      }
  1009.  
  1010.      if ( (len < 0) && !insdel )
  1011.      {
  1012.              /* request for sliding field, but INS and DEL are not valid,
  1013.                so treat this as an ordinary text field
  1014.                */
  1015.             len = abs(len);
  1016.             free (p);
  1017.             goto window_error;
  1018.      }
  1019.  
  1020.      if ( len < 0 )  /* save line 25 in case used by echoline */
  1021.      {
  1022.         if ( (sylvia = _fmalloc( 80 * sizeof(int) )) == (char far *) NULL )
  1023.             bomb0("_fmalloc error");
  1024.         MSJ_MovScrBuf( sylvia, 24, 0, 80, &video );
  1025.      }
  1026.  
  1027.  
  1028.      /* highlight and display string to be edited */
  1029.  
  1030.      reverse_video();
  1031.      ScrnMsg( q,x,y,winlen );
  1032.  
  1033.  
  1034.      /* go to beginning of edit field */
  1035.  
  1036.      setsynch(1);
  1037.      defcursor();
  1038.      gotoxy( x,y );
  1039.  
  1040.      /* set flag to see if the cursor ever stops in this field */
  1041.     
  1042.      field_cursor = 0;  /* FLAG, does cursor stop?  assume it never does */
  1043.  
  1044.  
  1045.  
  1046.      /* edit the copy of field in p, staying within window limits,
  1047.         that is, i is relative to q
  1048.         */
  1049.  
  1050.      i = 0;
  1051.      while (1)
  1052.      {
  1053.          /* test mask char, can it be edited? */
  1054.          if ( (cvalid == VNOP) || (*cvalid)( q[i], i ) )
  1055.          {
  1056.               if ( echoline && len < 0 )
  1057.               {
  1058.                     j = (q-p)+i;
  1059.                     ScrnMsg( p,0,24,maxlen ); /* echo entire line */
  1060.                     intense_video();          /* cursor attribute */
  1061.                     ScrnMsg( (p+j),j,24,1 );  /* echo cursor */
  1062.                     reverse_video();          /* back to edit attribute */
  1063.               }
  1064.  
  1065.               ch = getkey();        /* yes, get user's replacement */
  1066.               field_cursor = 1;     /* cursor finally landed! */
  1067.  
  1068.            top:
  1069.               switch ( ch )
  1070.               {
  1071.               case F1:
  1072.                     if ( len < 0 )
  1073.                     {
  1074.                         /* reasonable help is to turn the echoline
  1075.                            on or off
  1076.                            */
  1077.                         echoline ^= 1;
  1078.                         MSJ_MovBufScr( sylvia, 24, 0, 80, &video );
  1079.                     }
  1080.                     else
  1081.                     {
  1082.                           p[0] = ' ';        /* request assistance */
  1083.                         ch = CR;        /* must exit with field validation! */
  1084.                         goto top;
  1085.                     }
  1086.                     break;
  1087.  
  1088.               case Shift_F1:
  1089.               case Alt_F1:
  1090.               case Ctrl_F1:
  1091.                     if ( len < 0 )
  1092.                     {
  1093.                         MSJ_MovBufScr( sylvia, 24, 0, 80, &video );
  1094.                           p[0] = ' ';        /* request assistance */
  1095.                         ch = CR;        /* must exit with field validation! */
  1096.                         goto top;
  1097.                     }
  1098.                     /* all others fall into... */
  1099.  
  1100.               case F10:
  1101.                     version_info();
  1102.                     break;
  1103.               
  1104.               case F4:
  1105.                     tone ^= 1;      /* toggle sound effects */
  1106.                     break;
  1107.  
  1108.               case F3:              /* reset default field contents */
  1109.                     for ( i = 0; i < maxlen; i++ )
  1110.                     {
  1111.                         if ( cvalid == VNOP )
  1112.                             ch = fldptr[i];
  1113.                         else
  1114.                         {
  1115.                             ch = (*cvalid)( fldptr[i], i );
  1116.                         }
  1117.                          p[i] = ch ? ch : fldptr[i];
  1118.                     }
  1119.                     q = p;
  1120.                     i = 0;
  1121.                     ScrnMsg( q,x,y,winlen );
  1122.                     gotoxy( x+i,y );
  1123.                     break;
  1124.  
  1125.               case F9:              /* zap to end of field */
  1126.                     if ( insdel )
  1127.                     {
  1128.                         for ( j = (q-p) + i; j < maxlen; j++ )
  1129.                             *(p+j) = SPC;
  1130.                         ScrnMsg( q,x,y,winlen );
  1131.                         gotoxy( x+i,y );
  1132.                     }
  1133.                     else thurb();
  1134.                     break;
  1135.               
  1136.               case INS:             /* insert space at cursor */
  1137.                     if ( insdel )
  1138.                     {
  1139.                         for ( j = maxlen - 1, k = j - 1;
  1140.                               k >= (q-p)+i;
  1141.                               k--, j-- 
  1142.                             )
  1143.                             *(p + j) = *(p + k);
  1144.                         *(p + ((q-p)+i)) = SPC;
  1145.                         ScrnMsg( q,x,y,winlen );
  1146.                         gotoxy( x+i,y );
  1147.                     }
  1148.                     else thurb();
  1149.                     break;
  1150.  
  1151.               case DEL:             /* delete character at cursor */
  1152.                     if ( insdel )
  1153.                     {
  1154.                         for ( k = (q-p)+i, j = k + 1;
  1155.                               j < maxlen;
  1156.                               k++, j++
  1157.                             )
  1158.                             *(p + k) = *(p + j);
  1159.                         *(p + k) = SPC;
  1160.                         ScrnMsg( q,x,y,winlen );
  1161.                         gotoxy( x+i,y );
  1162.                     }
  1163.                     else thurb();
  1164.                     break;
  1165.  
  1166.               case '\x18':  /* Ctrl-X */
  1167.               case TAB:
  1168.                     ch = DN;
  1169.                     goto top;
  1170.                     break;
  1171.  
  1172.               case '\x5':  /* Ctrl-E */
  1173.               case Shift_TAB:
  1174.                     ch = UP;
  1175.                     goto top;
  1176.                     break;
  1177.  
  1178.               case UP:  /* exit WITHOUT field validation */
  1179.               case DN:
  1180.               case PGUP:
  1181.               case PGDN:
  1182.               case F2:
  1183.                     goto no_update;
  1184.                     break;
  1185.  
  1186.               case ESC:  /* exit WITH field validation */
  1187.               case CR:
  1188.                       goto update_edits;
  1189.                     break;
  1190.  
  1191.               case HOME:    /* go to start of field */
  1192.                     q = p;
  1193.                     ScrnMsg( q,x,y,winlen );
  1194.                     i = 0;
  1195.                     gotoxy( x+i,y );
  1196.                     break;
  1197.  
  1198.               case END:    /* go to end of text, or to end of field */
  1199.                     for ( j = maxlen-1; j > 0 && *(p+j) == SPC; j-- )
  1200.                     ;
  1201.                     if ( j == 0 && *p == SPC )
  1202.                         j = maxlen-1;
  1203.                     else if ( j == maxlen-1 )
  1204.                         ;
  1205.                     else
  1206.                         j++;
  1207.  
  1208.                     q = p;
  1209.                     i = winlen-1;
  1210.                     if ( (p+j) > (q+i) )
  1211.                     {
  1212.                         q = p+j-i;
  1213.                     }
  1214.                     else
  1215.                     {
  1216.                         i = j;
  1217.                     }
  1218.                     ScrnMsg( q,x,y,winlen );
  1219.                     gotoxy( x+i, y );
  1220.  
  1221.                     break;
  1222.               
  1223.               /* case '\x13': */ /* DOS interferes with Ctrl-S */
  1224.               case BS:        /* backspace or left arrow */
  1225.               case LF:
  1226.                     /* user's keypress behaves like UP if in position 0 */
  1227.  
  1228.                     if ( len < 0 )
  1229.                     {
  1230.                         if ( i == 0 )
  1231.                         {
  1232.                             --q;
  1233.                             if ( q < p )
  1234.                             {
  1235.                                 ch = UP;
  1236.                                 goto top;
  1237.                             }
  1238.                             ScrnMsg( q,x,y,winlen );
  1239.                         }
  1240.                         else
  1241.                         {
  1242.                             --i;
  1243.                         }
  1244.                         gotoxy( x+i,y );
  1245.                     }
  1246.                     else
  1247.                     {
  1248.                         if ( i == 0 )
  1249.                         {
  1250.                             ch = UP;
  1251.                             goto top;
  1252.                         }
  1253.  
  1254.                         /* otherwise, skip backwards... */
  1255.                           do
  1256.                         {
  1257.                             --i;
  1258.                               if ( i < 0 ) 
  1259.                                 i = 0;
  1260.                             gotoxy( x + i, y );
  1261.                             /* ...over non-edit chars in mask */
  1262.  
  1263.                             if ( cvalid == VNOP )
  1264.                                 break;
  1265.                         }
  1266.                         while ( i > 0 && (!(cvalid( p[i], i ))));
  1267.  
  1268.                         /* did mask bounce left put us in position 0? */
  1269.  
  1270.                         if ( cvalid == VNOP )
  1271.                             ;
  1272.                         else if ( i == 0 && (!(cvalid( p[i], i ))) )
  1273.                         {
  1274.                             ch = UP;
  1275.                             goto top;
  1276.                         }
  1277.                     }
  1278.                     break;
  1279.               
  1280.               case '\x4':   /* Ctrl-D */
  1281.               case RT:         /* right arrow */
  1282.                     ++i;
  1283.                     if ( len < 0 )
  1284.                     {
  1285.                         if ( i == winlen )
  1286.                         {
  1287.                             --i;
  1288.                             ++q;
  1289.                             if ( (q+winlen-1) > (p+maxlen-1) )
  1290.                             {
  1291.                                 if ( skip_to_next )
  1292.                                 {
  1293.                                     ch = CR;
  1294.                                     goto top;
  1295.                                 }
  1296.                                 --q;
  1297.                             }
  1298.                             ScrnMsg( q,x,y,winlen );
  1299.                         }
  1300.                     }
  1301.                     else
  1302.                     {
  1303.                         if ( i == maxlen )
  1304.                         {
  1305.                             if ( skip_to_next )
  1306.                             {
  1307.                                 ch = CR;
  1308.                                 goto top;
  1309.                             }
  1310.                             --i;
  1311.                         }
  1312.                     }
  1313.                     gotoxy ( x+i,y );
  1314.                     break;
  1315.  
  1316.               
  1317.               default:
  1318.                     if ( ch < ' ' || ch > '~' )
  1319.                     {
  1320.                         thurb();  /* non-ASCII key not defined above */
  1321.                     }
  1322.                       else if ((cvalid == VNOP) || (ch = (*cvalid)( ch, i )))
  1323.                     {
  1324.                         q[i] = ch;          /* put valid char in buffer */
  1325.                         rptchar(ch,1);      /* echo */
  1326.                         ++i;                /* advance index */
  1327.                         if ( len < 0 )
  1328.                         {
  1329.                             if ( i == winlen )
  1330.                             {
  1331.                                 --i;
  1332.                                 ++q;
  1333.                                 if ( (q+winlen-1) > (p+maxlen-1) )
  1334.                                 {
  1335.                                     if ( skip_to_next )
  1336.                                     {
  1337.                                         ch = CR;
  1338.                                         goto top;
  1339.                                     }
  1340.                                     --q;
  1341.                                 }
  1342.                                 ScrnMsg( q,x,y,winlen );
  1343.                             }
  1344.                         }
  1345.                         else
  1346.                         {
  1347.                             if ( i == maxlen )
  1348.                             {
  1349.                                 if ( skip_to_next )
  1350.                                 {
  1351.                                     ch = CR;
  1352.                                     goto top;
  1353.                                 }
  1354.                                 else  /* end of field, but need explicit exit */
  1355.                                 {
  1356.                                     --i;
  1357.                                 }
  1358.                             }
  1359.                         }
  1360.                         gotoxy( x+i,y );    /* advance cursor */
  1361.                     }
  1362.                     else  /* invalid key */
  1363.                     {
  1364.                         thurb();
  1365.                     }
  1366.                     break;
  1367.               }
  1368.          }
  1369.          else  /* not a "valid" char in mask, so accept "input" from buffer */ 
  1370.          {
  1371.              rptchar( q[i],1 );         /* echo mask character */
  1372.              ++i;                       /* advance index */
  1373.              gotoxy( x+i,y );           /* advance cursor */
  1374.              if ( (q-p)+i == maxlen )    /* last pos in field cannot be edited (?!) */
  1375.              {
  1376.                 if ( !field_cursor )
  1377.                 {
  1378.                     ch = END;    /* slipped through mask, no edits! */
  1379.                     goto no_update;
  1380.                 }
  1381.                 else if ( skip_to_next )
  1382.                 {
  1383.                      ch = DN;     /* cursor will SLIP out of field! */
  1384.                     goto top;
  1385.                 }
  1386.                 else
  1387.                 {
  1388.                      ch = LF;     /* cursor will BOUNCE to previous pos! */
  1389.                     goto top;
  1390.                 }
  1391.              }
  1392.          }
  1393.      }
  1394.  
  1395.      /* validate the entire field, when done... */
  1396.  
  1397. update_edits:  /* Esc, Enter */
  1398.  
  1399.      /* programmer stupidity check #2; did the field validater write garbage
  1400.         over the receiving field?  note: p is known to be a string, but 
  1401.         fldptr is only known to be an array of char maxlen in size (even
  1402.         though fldptr probably is a string, we can't assume that's true!)
  1403.         */
  1404.      if ( strlen( p ) != maxlen )  /* too long or too short? */
  1405.         bomb0("field size violation: %d [%s]", maxlen, p );
  1406.  
  1407.      /* good string in field buffer, continue with normal exit */
  1408.  
  1409.      if (fvalid == VNOP) 
  1410.      {
  1411.         if (*p == ' ') /* user requested assistance on no-validate field */
  1412.         {
  1413.             boxmsg( "Could be anything!" ); /* give permission */
  1414.             ch = 0;
  1415.         }
  1416.         else /* good field by definition */
  1417.         {
  1418.             memcpy( fldptr, p, maxlen );
  1419.         }
  1420.      }
  1421.      else if ( (*fvalid)( p,x,y,maxlen ) )
  1422.      {
  1423.         memcpy( fldptr, p, maxlen );
  1424.      }
  1425.      else
  1426.      {
  1427.          ch = 0;
  1428.      }
  1429.  
  1430.      } while ( ch == 0 ); /* F1 assistance loop ends here, 9/3/88, dco */
  1431.  
  1432. no_update: /* End, PgUp, PgDn, Up, Down */
  1433.  
  1434.      DispIntense( fldptr,x,y,winlen ); /* show true field contents on exit */
  1435.      normal_video();
  1436.      setsynch(temp);
  1437.  
  1438.      if ( len < 0 )  /* restore line 25 used by echoline */
  1439.      {
  1440.         MSJ_MovBufScr( sylvia, 24, 0, 80, &video );
  1441.         /* Good night, Mrs. Calabash, wherever you are */
  1442.         _ffree(sylvia);
  1443.      }
  1444.  
  1445.      free( p );      /* end of field copy requirement */
  1446.      return ( ch );     /* inform caller how edit was terminated */
  1447. }
  1448.  
  1449.  
  1450.  
  1451.  
  1452. /*  EditForm returns these exit codes:       SAVE_FORM, SKIP_FORM,
  1453. **  plus, IFF MultiPage:                     PGDN, PGUP,
  1454. **  plus, IFF cursor did not stop anywhere:  END
  1455. */
  1456. unsigned EditForm( FORM_RULES form[], int NumFields, int MultiPage )
  1457. {
  1458.     char **pp, *p;
  1459.     int i, go, x, y, len, winlen, maxlen, cursor_landed, temp, action;
  1460.     unsigned ExitKey;
  1461.     VALIDATER fi;
  1462.     VALIDATER cv;
  1463.     VALIDATER fv;
  1464.  
  1465.     if ( NumFields < 1 )
  1466.         return ( END );  /* bad domain, assume caller will abort */
  1467.     
  1468.     /* show all fields */
  1469.  
  1470.     temp = getsynch();
  1471.     setsynch(0);
  1472.  
  1473.     for ( i = 0; i < NumFields; i++ )
  1474.     {
  1475.         pp  = form[i].fptr;
  1476.         p   = *pp;
  1477.         x   = form[i].x;
  1478.         y   = form[i].y;
  1479.         len = form[i].len;
  1480.  
  1481.         if ( len < 0 )
  1482.         {
  1483.             maxlen = MAXVFLDLEN;
  1484.             winlen = abs(len);
  1485.         }
  1486.         else
  1487.         {
  1488.             maxlen = winlen = len;
  1489.         }
  1490.  
  1491.         if ( (fi = form[i].fi) != VNOP )
  1492.             (*fi)( p,x,y,maxlen );  /* set up virtual fields, etc. */
  1493.  
  1494.         DispIntense( p,x,y,winlen );
  1495.     }
  1496.  
  1497.     /* edit fields in FORM_RULES order */
  1498.  
  1499.     cursor_landed = 0;  /* assume it never does */
  1500.     do
  1501.     {
  1502.         i = 0;
  1503.         go = 1;
  1504.         while ( go )
  1505.         {
  1506.             pp  = form[i].fptr;
  1507.             p   = *pp;
  1508.             x   = form[i].x;
  1509.             y   = form[i].y;
  1510.             len = form[i].len;
  1511.             cv  = form[i].cv;
  1512.             fv  = form[i].fv;
  1513.  
  1514.             if ( len < 0 )
  1515.             {
  1516.                 maxlen = MAXVFLDLEN;
  1517.                 winlen = abs(len);
  1518.             }
  1519.             else
  1520.             {
  1521.                 maxlen = winlen = len;
  1522.             }
  1523.  
  1524.             if ( (fi = form[i].fi) != VNOP )
  1525.             {
  1526.                 (*fi)( p,x,y,maxlen );     /* initialize the field */
  1527.                 DispIntense( p,x,y,winlen ); /* show changes, if any */
  1528.             }
  1529.  
  1530.             if ( cv == VNOP )     /* display-only or computed field? */
  1531.             {
  1532.                 if ( fv != VNOP )
  1533.                 {
  1534.                     (*fv)( p,x,y,maxlen );     /* validate or compute the field */
  1535.                     DispIntense( p,x,y,winlen ); /* show any changes, if computed */
  1536.                 }
  1537.                 ExitKey = END;
  1538.             }
  1539.             else
  1540.             {
  1541.                 ExitKey = EditField( p, x, y, len, SKIP_OK, cv, fv );
  1542.  
  1543.                 if ( ExitKey != END )
  1544.                     cursor_landed = 1;  /* cursor stopped SOMEWHERE! */
  1545.             }
  1546.  
  1547.             /* is user's field exit key a form exit key? */
  1548.  
  1549.             switch ( ExitKey )
  1550.             {
  1551.             default:  /* case 0 is obsolete */
  1552.                         break;
  1553.  
  1554.             case F2:
  1555.                         ExitKey = ESC;  /* fall into */
  1556.             case ESC:
  1557.                         /* decide_form_exit() will determine exit code */
  1558.                         go = 0;
  1559.                         break;
  1560.  
  1561.             case PGDN:
  1562.             case PGUP:
  1563.                         if ( MultiPage )
  1564.                         {
  1565.                             action = ExitKey;  /* PgUp or PgDn, slide off */
  1566.                             goto zoo;
  1567.                         }
  1568.                         else if ( ExitKey == PGUP )  /* top of form */
  1569.                             i = 0;
  1570.                         else                         /* bottom of form */
  1571.                             i = NumFields - 1;
  1572.                         break;
  1573.  
  1574.             case UP:
  1575.                     do
  1576.                     {
  1577.                         --i;
  1578.                         if ( i < 0 )
  1579.                             i = NumFields - 1;
  1580.                     }
  1581.                     while ( form[i].cv == VNOP );
  1582.                     break;
  1583.  
  1584.             case DN:
  1585.             case CR:
  1586.                     ++i;
  1587.                     if ( i == NumFields )
  1588.                     {
  1589.                         i = 0;
  1590.  
  1591.                         if ( MultiPage )
  1592.                         {
  1593.                             action = PGDN; /* slide off form */
  1594.                             goto zoo;
  1595.                         }
  1596.                     }
  1597.                     break;
  1598.         
  1599.             case END:  /* user cannot present this value to switch */
  1600.                     ++i;
  1601.                     if ( i == NumFields )
  1602.                     {
  1603.                         i = 0;
  1604.                         if ( cursor_landed == 0 ) /* no edits by end of form? */
  1605.                         {
  1606.                             action = END;
  1607.                                goto zoo;
  1608.                         }
  1609.                         else if ( MultiPage )
  1610.                         {
  1611.                             action = PGDN;  /* slide off form */
  1612.                             goto zoo;
  1613.                         }
  1614.                     }
  1615.                     break;
  1616.             }
  1617.         }
  1618.     }
  1619.     while ( (action = decide_form_exit()) == EDIT_FORM );
  1620.  
  1621.     zoo:
  1622.     setsynch(temp);
  1623.     return (action);
  1624. }
  1625.  
  1626.  
  1627.  
  1628. /* =========================  Screen Managers  ========================== */
  1629.  
  1630.  
  1631. #define ptrseg(P) ((int)(((long)P)>>16))
  1632. #define ptrofs(P) ((int)((long)P))
  1633.  
  1634. /*  _onepageform returns:  SAVE_FORM, SKIP_FORM
  1635. */
  1636.  
  1637. int _onepageform( char far *screen, FORM_RULES Page[], int NumFields )
  1638. {
  1639.     unsigned result;
  1640.     int i,j,action;
  1641.  
  1642.     if ( screen != (char far *) NULL )
  1643.     {
  1644.         PopScreen( screen );
  1645.     }
  1646.  
  1647.     do
  1648.     {
  1649.         result = EditForm( Page, NumFields, SINGLEPAGE );
  1650.         if ( result == END )
  1651.         {
  1652.             action = SKIP_FORM; /* null form, cursor never stopped */
  1653.         }
  1654.         else
  1655.         {
  1656.             action = result;
  1657.         }
  1658.     }
  1659.     while ( !(action == SAVE_FORM || action == SKIP_FORM) );
  1660.  
  1661.     return ( action );
  1662. }
  1663.  
  1664.  
  1665.  
  1666. /*  _twopageform returns:  SAVE_FORM, SKIP_FORM
  1667. */
  1668.  
  1669. int _twopageform( char far *screen1, FORM_RULES Page1[], int NumFields1,
  1670.                   char far *screen2, FORM_RULES Page2[], int NumFields2 )
  1671. {
  1672.     char far *scrfptr;
  1673.     unsigned result;
  1674.     int i, j, action, flag, cursor_landed;
  1675.  
  1676.     cursor_landed = 0;  /* assume cursor never lands on either page */
  1677.  
  1678.     do
  1679.     {
  1680.         if ( screen1 != (char far *) NULL )
  1681.         {
  1682.             PopScreen( screen1 );
  1683.         }
  1684.  
  1685.         result = EditForm( Page1, NumFields1, MULTIPAGE );
  1686.         if ( result == PGUP || result == PGDN || result == END )
  1687.         {
  1688.             action = EDIT_FORM;
  1689.             if ( result != END )
  1690.                 cursor_landed = 1;
  1691.         }
  1692.         else  /* anything else exits from entire form */
  1693.         {
  1694.             action = result;
  1695.             continue;
  1696.         }
  1697.  
  1698.         if ( screen2 != (char far *) NULL )
  1699.         {
  1700.             PopScreen( screen2 );
  1701.         }
  1702.  
  1703.         result = EditForm( Page2, NumFields2, MULTIPAGE );
  1704.         if ( result == PGUP || result == PGDN || result == END )
  1705.         {
  1706.             action = EDIT_FORM;
  1707.             if ( result != END )
  1708.                 cursor_landed = 1;
  1709.         }
  1710.         else  /* anything else exits from entire form */
  1711.         {
  1712.             action = result;
  1713.         }
  1714.  
  1715.         if ( cursor_landed == 0 )  /* cursor never landed on either page */
  1716.             action = SKIP_FORM;
  1717.     }
  1718.     while ( !(action == SAVE_FORM || action == SKIP_FORM ) );
  1719.  
  1720.     return ( action );
  1721. }
  1722.  
  1723.  
  1724.  
  1725. /*  OnePageForm returns:  SAVE_FORM, SKIP_FORM,
  1726. **  plus, IFF OldRecord:  DELETE_FORM, STOP_SEARCH
  1727. */
  1728.  
  1729. int OnePageForm( char *ScreenName, FORM_RULES Page[], int NumFields, 
  1730.                  int OldRecord )
  1731. {
  1732.     char far *screen;
  1733.     int action;
  1734.  
  1735.     if ( ScreenName != (char *)NULL )
  1736.         screen = PrepScreen( ScreenName );
  1737.  
  1738.     if ( OldRecord )
  1739.     {
  1740.         DisplayForm( screen, Page, NumFields );
  1741.         if ( (action = opening_dialogue()) != EDIT_FORM )
  1742.             goto zoo;
  1743.     }
  1744.         
  1745.     action = _onepageform( screen, Page, NumFields );
  1746.  
  1747.     zoo:
  1748.     if ( ScreenName != (char *)NULL )
  1749.         _ffree(screen);
  1750.  
  1751.     return (action);
  1752. }
  1753.  
  1754.  
  1755. /*  TwoPageForm returns:  SAVE_FORM, STOP_SEARCH,
  1756. **  plus, IFF OldRecord:  SKIP_FORM, DELETE_FORM
  1757. */
  1758.  
  1759. int TwoPageForm( char *ScreenName1, FORM_RULES Page1[], int NumFields1, 
  1760.                  char *ScreenName2, FORM_RULES Page2[], int NumFields2, 
  1761.                  int OldRecord )
  1762. {
  1763.     char far *screen1;
  1764.     char far *screen2;
  1765.     int action;
  1766.  
  1767.     if ( ScreenName1 != (char *)NULL )
  1768.         screen1 = PrepScreen( ScreenName1 );
  1769.     if ( ScreenName2 != (char *)NULL )
  1770.         screen2 = PrepScreen( ScreenName2 );
  1771.  
  1772.     if ( OldRecord )
  1773.     {
  1774.         DisplayForm( screen1, Page1, NumFields1 );
  1775.         if ( (action = opening_dialogue()) != EDIT_FORM )
  1776.             goto zoo;
  1777.     }
  1778.         
  1779.     action = _twopageform( screen1, Page1, NumFields1, 
  1780.                            screen2, Page2, NumFields2 );
  1781.  
  1782.     zoo:
  1783.     if ( ScreenName2 != (char *) NULL )
  1784.         _ffree(screen2);
  1785.     if ( ScreenName1 != (char *) NULL )
  1786.         _ffree(screen1);
  1787.  
  1788.     return (action);
  1789. }
  1790.  
  1791.  
  1792.  
  1793. /* ======================  Initialize a Form ==================== */
  1794.  
  1795. /* copies up to len bytes of each field's default string into its 
  1796.    actual field buffer, in the range of fields indicated; if the
  1797.    default (source) is shorter than the field itself (destination),
  1798.    the remainder of the field is padded to len bytes with blanks
  1799.    */
  1800. void InitForm( FORM_RULES Form[], int NumFields )
  1801. {
  1802.     int i, j, len;
  1803.     char **p, *src, *dst;
  1804.  
  1805.     for ( i = 0; i < NumFields; i++ )
  1806.     {
  1807.         p   = Form[i].fptr;
  1808.         dst = *p;
  1809.         src = Form[i].dflt;
  1810.         len = Form[i].len;
  1811.  
  1812.         memset( dst, SPC, len );     /* clear the field to blanks */
  1813.  
  1814.         if ( src != (char *) NULL )  /* set the field to its default */
  1815.         {
  1816.             for ( j = 0; j < len && *( src + j ); j++ )
  1817.             {
  1818.                 *( dst + j ) = *( src + j );
  1819.             }
  1820.         }
  1821.     }
  1822. }
  1823.  
  1824.  
  1825.  
  1826. /* dynamically allocates the indicated range of fields in a form,
  1827.    and initializes each to a null-terminated string of len blanks;
  1828.    if the dflt field is not NULL, initializes the field's mask 
  1829.    */
  1830.  
  1831. static int AllocatedFlag = 0;
  1832.  
  1833. /* On exit, a program may test to see whether DamageControlBuffer is still
  1834.    DamageControlMsg.  If it isn't, there was a write to a field which has
  1835.    been Released somewhere in the program.  However, these are not declared
  1836.    in "fafnir.h"!  (This is handled by bomb0().  9/30/88, d.c.oshel )
  1837.    */
  1838.  
  1839. void AllocateFields( FORM_RULES form[], int NumFields )
  1840. {
  1841.     int i, j, len;
  1842.     char **p, *src, *dst;
  1843.  
  1844.     for ( i = 0; i < NumFields; i++ )
  1845.     {
  1846.         len = form[i].len;
  1847.  
  1848.         if ( len < 0 )
  1849.             len = MAXVFLDLEN;
  1850.  
  1851.         p   = form[i].fptr;
  1852.         *p = (char *) malloc( (len+1) +4 ); /* 4 luck! I don't trust Microsoft's malloc() */
  1853.         if ( *p == (char *) NULL )
  1854.         {
  1855.             bomb0("alloc field: %s", dynmemerr);
  1856.         }
  1857.         dst = *p;
  1858.  
  1859.         memset( dst, '\0', len+1 );  /* put '\0's to end of string */
  1860.         memset( dst, SPC, len );     /* clear the field to blanks */
  1861.  
  1862.         src = form[i].dflt;
  1863.  
  1864.         if ( src != (char *) NULL )  /* set the field to its default */
  1865.         {
  1866.             for ( j = 0; j < len && *( src + j ); j++ )
  1867.             {
  1868.                 *( dst + j ) = *( src + j );
  1869.             }
  1870.         }
  1871.     }
  1872.     AllocatedFlag = 1;
  1873. }
  1874.  
  1875.  
  1876.  
  1877.  
  1878. /* releases a form's dynamic memory allocation as set by AllocateFields()
  1879.    */
  1880. void ReleaseFields( FORM_RULES form[], int NumFields )
  1881. {
  1882.     int i;
  1883.     char **p;
  1884.  
  1885.     if ( AllocatedFlag )
  1886.     {
  1887.         for ( i = 0; i < NumFields; i++ )
  1888.         {
  1889.             p = form[i].fptr;
  1890.             free (*p);
  1891.             *p = &DamageControlBuffer[0];
  1892.         }
  1893.         AllocatedFlag = 0;
  1894.     }
  1895. }
  1896.  
  1897.  
  1898.